package org.breathe.command.impl.zset;

import org.breathe.command.Command;
import org.breathe.command.CommandType;
import org.breathe.datastruct.RedisBytes;
import org.breathe.datastruct.RedisData;
import org.breathe.datastruct.impl.RedisZSet;
import org.breathe.internal.SkipList;
import org.breathe.protocal.Resp;
import org.breathe.protocal.decode.BulkString;
import org.breathe.protocal.decode.Errors;
import org.breathe.protocal.decode.RespArray;
import org.breathe.server.core.RedisCore;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author: breathe
 * @createTime: 2025-05-16
 */
public class ZRange implements Command {
    private final RedisCore redisCore;
    private RedisBytes key;
    private int start;
    private int end;
    private boolean withScores = false;
    public ZRange(RedisCore redisCore) {
        this.redisCore = redisCore;
    }
    @Override
    public CommandType getType() {
        return CommandType.ZRANGE;
    }

    @Override
    public void setContext(Resp[] array) {
        if (array.length < 4) {

            throw new IllegalArgumentException("zrange command require at least 4 arguments");
        }
        key = ((BulkString)array[1]).getContent();
        start = Integer.parseInt(((BulkString)array[2]).getContent().getString());
        end = Integer.parseInt(((BulkString)array[3]).getContent().getString());
        if (array.length == 5) {
            withScores = "WITHSCORES".equals(((BulkString)array[4]).getContent().getString());
        }
    }

    @Override
    public Resp handle() {
        try {
            RedisData redisData = redisCore.get(key);
            if (redisData == null) {
                return new RespArray(new Resp[0]);
            }
            if (redisData instanceof RedisZSet redisZset) {
                int size = redisZset.size();
                if (size == 0) {
                    return new RespArray(new Resp[0]);
                }
                int startIdx = start;
                int endIdx = end;
                if (startIdx < 0) {
                    startIdx = size + startIdx;
                }
                if (endIdx < 0) {
                    endIdx = size + endIdx;
                }
                startIdx = Math.max(0, startIdx);
                endIdx = Math.min(size - 1, endIdx);
                if (startIdx > endIdx) {
                    return new RespArray(new Resp[0]);
                }
                List<SkipList.SkipListNode<String>> range = redisZset.getRankRange(startIdx, endIdx);
                List<Resp> respList = new ArrayList<>();
                for (SkipList.SkipListNode<String> node : range) {
                    respList.add(new BulkString(new RedisBytes(node.member.getBytes())));
                    if (withScores) {
                        respList.add(new BulkString(new RedisBytes(String.valueOf(node.score).getBytes())));
                    }
                }
                return new RespArray(respList.toArray(new Resp[0]));
            }
        } catch (Exception e) {
            return new Errors("command run fail");
        }
        return new Errors("command run fail");
    }

    @Override
    public boolean writeCommand() {
        return false;
    }
}
